#include "server.h"

static struct
{/*{{{*/
    EP ep;
    EP_SLOT server;
    EP_SLOT monitor;
    EP_SLOT monitor_login;
    char *respond;
}/*}}}*/
worker_ctl;

static unsigned int quit;

#define MONITOR_IDLE_TIMEOUT    SECOND_OF_MINUTE(5) /* 监控闲置超时 */

typedef int (*P_CMD_FUNC)(char *, char *);
#define CMD_FUNC(svc)   \
int CMD_FUNC_##svc(char *request, char *respond)

static CMD_FUNC(HELP);
static CMD_FUNC(SHUTDOWN);
static CMD_FUNC(PRINTCLIENT);
static CMD_FUNC(PRINTSERVICE);

static struct _cmd_map
{/*{{{*/
    char *name;
    char *ab;
    P_CMD_FUNC pfunc;
    char *desc;
    char *usage;
}/*}}}*/
cmd_map[]=
{/*{{{*/
    {
        "HELP", "HELP", CMD_FUNC_HELP,
        "",
        ""
    },
    {
        "SHUTDOWN", "SHUTDOWN", CMD_FUNC_SHUTDOWN,
        "shutdown server",
        "shutdown"
    },
    {
        "PRINTCLIENT", "PCLT", CMD_FUNC_PRINTCLIENT,
        "print client ip & port",
        "printclient(pclt)"
    },
    {
        "PRINTSERVICE", "PSVC", CMD_FUNC_PRINTSERVICE,
        "print service status",
        "printservice(psvc)"
    }
};/*}}}*/
static unsigned int CMD_MAP_SIZE=
sizeof(cmd_map)/sizeof(struct _cmd_map);

static CMD_FUNC(HELP)
{/*{{{*/
    struct _cmd_map *map=NULL;
    int offset=0;
    int i;

    // 所有帮助
    if(request)
        while(*request == SPACE)
            request++;
    if(!request || BUF_IS_CLR(request))
    {
        offset+=sprintf(respond+offset, "help [command]\n");
        offset+=sprintf(respond+offset, "commands:\n");
        for(i=1; i<CMD_MAP_SIZE; ++i)
            offset+=sprintf(respond+offset, "    %s\n",
                    cmd_map[i].name);
        return(offset);
    }

    // 单独命令帮助
    for(i=1; i<CMD_MAP_SIZE; ++i)
    {
        map=&cmd_map[i];
        if(strcasecmp(map->name, request) == 0 ||
                strcasecmp(map->ab, request) == 0)
        {
            offset+=sprintf(respond+offset, "Description: %s\n",
                    map->desc);
            offset+=sprintf(respond+offset, "Usage: %s\n",
                    map->usage);
            return(offset);
        }
    }

    offset+=sprintf(respond+offset, "Command not found, type help\n");
    return(offset);
}/*}}}*/

static CMD_FUNC(SHUTDOWN)
{/*{{{*/
    int offset=0;

    offset+=sprintf(respond+offset,
            "Shutdown complete, quitting...");
    quit=IS_TRUE;

    return(offset);
}/*}}}*/

static CMD_FUNC(PRINTCLIENT)
{/*{{{*/
    int offset=0;
    int i;

    offset+=sprintf(respond+offset,
            "Client                         Calls\n");
    offset+=sprintf(respond+offset,
            "-------                        -------\n");
    for(i=0; i<EP_POLL_MAX_NUM; ++i)
    {
        struct _client_info *client=&stat_info._worker_.client[i];
        if(client->ip)
        {
            char buf[50];
            sprintf(buf, "%s:%d", client->ip, client->port);
            offset+=sprintf(respond+offset,
                    "%-30s %d\n",
                    buf, client->call_num);
        }
    }
    offset+=sprintf(respond+offset,
            "-----------\n"
            "Total: %d",
            stat_info._worker_.client_num);

    return(offset);
}/*}}}*/

static CMD_FUNC(PRINTSERVICE)
{/*{{{*/
    int offset=0;
    int i;

    offset+=sprintf(respond+offset,
            "Service                        Calls\n");
    offset+=sprintf(respond+offset,
            "-------                        -------\n");
    for(i=0; i<SVC_MAP_SIZE; ++i)
    {
        struct _svc_info *svc=&stat_info._svc_.svc[i];
        offset+=sprintf(respond+offset,
                "%-30s %d\n",
                svc->name, svc->call_num);
    }

    return(offset);
}/*}}}*/

static int _callcmd(char *request, char *respond)
{/*{{{*/
    int request_size=strlen(request);
    int offset=0;
    char *body;
    int svcsize;
    struct _cmd_map *map=NULL;

    if(request[request_size-1] == RTN)
    {
        if(request[request_size-2] == '\r')
            request[request_size-2]=ZERO;
        else
            request[request_size-1]=ZERO;
    }
    else
        request[request_size]=ZERO;
    trim_space(request);
#ifdef _MY_DEBUG
    errlog("%s: request(%s)", __func__, request);
#endif

    body=strchr(request, SPACE);
    if(body)
    {
        svcsize=body-request;
        while(*body == SPACE) body++;
    }
    else
        svcsize=strlen(request);
    if(svcsize > 0 && svcsize <= NAME_SIZE)
    {
        char svc[NAME_SIZE+1];
        int i;

        strncpy(svc, request, svcsize);
        svc[svcsize]=ZERO; 
#ifdef _MY_DEBUG
        errlog("%s: svc(%s)", __func__, svc);
#endif
        for(i=0; i<CMD_MAP_SIZE; ++i)
        {
            map=&cmd_map[i];
            if(strcasecmp(map->name, svc) == 0 ||
                    strcasecmp(map->ab, svc) == 0)
                break;
        }
        if(i == CMD_MAP_SIZE)
        {
            offset+=sprintf(respond+offset, "command(%s) not found",
                    svc);
#ifdef _MY_DEBUG
            errlog("%s: %s", __func__, respond);
#endif
            offset+=sprintf(respond+offset, "\n");
        }
        else
        {
            offset+=map->pfunc(body, respond);
        }
    }
    else
    {
        offset+=sprintf(respond+offset, "command invalid");
#ifdef _MY_DEBUG
        errlog("%s: %s", __func__, respond);
#endif
        offset+=sprintf(respond+offset, "\n");
    }
    sprintf(respond+offset, TEXTEND);

    return(0);
}/*}}}*/

static int _callsvc(char *request)
{/*{{{*/
    struct svc_map *map=NULL;
    char *respond=worker_ctl.respond;
    char *body;
    char *svc;
    int svc_idx;
    int rtn;
    int res;

    CLR_BUF(respond);
    body=request;
    if((svc=para_get_item(request, &body)) == NULL ||
            (res=strlen(svc)) <= 0 || res > NAME_SIZE)
    {
        errlog("%s: svc(%s) invalid",
                __func__, (svc)?svc:SPACES);
		para_put_item(respond, ERR_SVC_INVALID);
        return(atoi(ERR_SVC_INVALID));
    }

    for(svc_idx=0; svc_idx<SVC_MAP_SIZE; ++svc_idx)
    {
        map=&SVC_MAP[svc_idx];
        if(strcasecmp(map->name, svc) == 0) break;
    }
    if(svc_idx == SVC_MAP_SIZE)
    {
        errlog("%s: svc(%s) not found", __func__, svc);
        para_put_item(respond, ERR_SVC_NOT_FOUND);
        rtn=atoi(ERR_SVC_NOT_FOUND);
    }
    else
    {
        struct _svc_info *svc=&stat_info._svc_.svc[svc_idx];
        rtn=map->pfunc(body, respond);
        ++svc->call_num;
    }

    return(rtn);
}/*}}}*/

static int _conn(struct ep_active_slot *active_slot)
{/*{{{*/
    SOCKET new_sock=active_slot->_u._conn.sock;
    EP_SLOT new_slot;
    int res;

    if(active_slot->slot == worker_ctl.monitor)
    {
        if(worker_ctl.monitor_login)
        {
            sock_close(new_sock);
            errlog("%s: monitor has logged in", __func__);
            return(-1);
        }
    }
    if((res=ep_slot_register(worker_ctl.ep, new_sock, IS_FALSE,
                    &new_slot)))
    {
        errlog("%s : ep_slot_register fail, res:%d", __func__, res);
        sock_close(new_sock);
        return(-1);
    }
    if(active_slot->slot == worker_ctl.monitor)
    {
        ep_slot_set_timeout(new_slot, MONITOR_IDLE_TIMEOUT);
        worker_ctl.monitor_login=new_slot;
        errlog("%s: monitor log in", __func__);
    }
    else
    {
        struct ep_slot_info slot;
        struct _client_info *client;

        ep_slot_set_wrap(new_slot);
        ep_get_slot_info(worker_ctl.ep, new_slot, &slot);
        client=&stat_info._worker_.client[slot.slot_idx];
        client->ip=strdup(slot.sock_info.peer_ip);
        client->port=slot.sock_info.peer_port;
        client->call_num=0;
        ++stat_info._worker_.client_num;
        ++stat_info._worker_.conn_served;
        errlog("%s: client log in, total:%d",
                __func__, stat_info._worker_.client_num);
    }

    return(0);
}/*}}}*/

static int _read(struct ep_active_slot *active_slot)
{/*{{{*/
    char *request=active_slot->_u._read.msg;
    char *respond=worker_ctl.respond;
    int respond_size;
    int res;

    request[active_slot->_u._read.msg_size]=ZERO;
    if(active_slot->slot == worker_ctl.monitor_login)
    {
        if((res=_callcmd(request, respond)))
        {
            errlog("%s: _callcmd fail, res:%d", __func__, res);
        }
    }
    else
    {
        struct _client_info *client=
            &stat_info._worker_.client[active_slot->slot_idx];

        res=_callsvc(request);
        if(res)
        {
            struct ep_slot_info slot_info;
            ep_get_slot_info(worker_ctl.ep, active_slot->slot,
                    &slot_info);
            errlog("%s: svc_call res:%d, from(%s:%d)",
                    __func__, res,
                    slot_info.sock_info.peer_ip,
                    slot_info.sock_info.peer_port);
        }
        ++client->call_num;
    }

    respond_size=strlen(respond);
    if((res=ep_write(worker_ctl.ep, active_slot->slot,
                    respond, &respond_size)) != 0)
    {
        errlog("%s: ep_write fail, res:%d", __func__, res);
        return(-1);
    }

    return(0);
}/*}}}*/

static int _disconn(struct ep_active_slot *active_slot,
                        char *type)
{/*{{{*/
    if(active_slot->slot == worker_ctl.monitor_login)
    {
        worker_ctl.monitor_login=NULL;
        errlog("%s: monitor %s, "
                "revoke slot(sock:%d, idx:%d), res:%d",
                __func__, type,
                active_slot->_u._abort.sock,
                active_slot->slot_idx,
                active_slot->_u._abort.error);
    }
    else
    {
        struct _client_info *client=
            &stat_info._worker_.client[active_slot->slot_idx];
        FREE(client->ip);
        --stat_info._worker_.client_num;
        errlog("%s: client %s, total:%d, "
                "revoke slot(sock:%d, idx:%d), res:%d",
                __func__, type, stat_info._worker_.client_num,
                active_slot->_u._abort.sock,
                active_slot->slot_idx,
                active_slot->_u._abort.error);
    }

    return(0);
}/*}}}*/

static int _worker()
{/*{{{*/
    struct ep_active_slot *active_list;
    int res;

    while(!quit)
    {
        res=ep_poll(worker_ctl.ep, &active_list);
        if(res)
        {
            errlog("%s: revoke listener, res:%d, impossible",
                    __func__, res);
            continue;
        }
        while(active_list)
        {
            switch(active_list->type)
            {
                case EP_SLOT_CONN:
                    _conn(active_list);
                    break;
                case EP_SLOT_READ:
                    _read(active_list);
                    break;
                case EP_SLOT_ABORT:
                    _disconn(active_list, "abort");
                    break;
                case EP_SLOT_TIMEOUT:
                    _disconn(active_list, "timeout");
                    break;
                default:
                    errlog("%s: poll type(%s) invalid, impossible",
                            __func__, active_list->type);
                    break;
            }
            EP_ACTIVE_LIST_NEXT(active_list);
        }
    }

    errlog("%s: shutdown", __func__);
    return(0);
}/*}}}*/

int worker_start()
{/*{{{*/
    SOCKET sock;
    int i;
    int res;

    // stat_info
    for(i=0; i<EP_POLL_MAX_NUM; ++i)
    {
        struct _client_info *client=&stat_info._worker_.client[i];
        client->ip=NULL;
        client->port=0;
        client->call_num=0;
    }
    stat_info._worker_.client_num=0;
    stat_info._worker_.conn_served=0;

    ALLOC_MULTI_DOERR(stat_info._svc_.svc, struct _svc_info, SVC_MAP_SIZE,
            fprintf(stderr, "%s: alloc stat_info._svc_.svc\n", __func__);
            return(-1));
    for(i=0; i<SVC_MAP_SIZE; ++i)
    {
        struct _svc_info *svc=&stat_info._svc_.svc[i];
        svc->name=SVC_MAP[i].name;
        svc->call_num=0;
    }

    quit=IS_FALSE;

    /*----- worker -----*/
    ALLOC_MULTI_DOERR(worker_ctl.respond, char, SOCK_MSG_SIZE,
            fprintf(stderr, "%s: alloc respond fail\n", __func__);
            return(-1));

    if((res=ep_create(&worker_ctl.ep)))
    {
        fprintf(stderr, "%s: ep_create fail, res:%d\n", __func__, res);
        return(-1);
    }
    // server port
    if((sock=sock_listen(cnfg_info._access_.server_port)) ==
            INVALID_SOCKET)
    {
        fprintf(stderr, "%s: open server port(%d) fail\n",
                __func__, cnfg_info._access_.server_port);
        return(-1);
    }
    if((res=ep_slot_register(worker_ctl.ep, sock, IS_TRUE,
                    &worker_ctl.server)))
    {
        fprintf(stderr,
                "%s: ep_slot_register server port(%d) fail, res:%d\n",
                __func__, cnfg_info._access_.server_port, res);
        return(-1);
    }
    // monitor port
    if((sock=sock_listen(cnfg_info._access_.monitor_port)) ==
            INVALID_SOCKET)
    {
        fprintf(stderr, "%s: open monitor port(%d) fail\n",
                __func__, cnfg_info._access_.monitor_port);
        return(-1);
    }
    if((res=ep_slot_register(worker_ctl.ep, sock, IS_TRUE,
                    &worker_ctl.monitor)))
    {
        fprintf(stderr,
                "%s: ep_slot_register monitor port(%d) fail, res:%d\n",
                __func__, cnfg_info._access_.monitor_port, res);
        return(-1);
    }

    errlog("server start");
    return(_worker());
}/*}}}*/
